<!DOCTYPE html>
<html class="writer-html5" lang="zh" >
<head>
  <meta charset="utf-8" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />

  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Ch6-2 动态渲染 &mdash; EasyVulkan</title>
      <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
      <link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
      <link rel="stylesheet" href="_static/theme.css" type="text/css" />
  <!--[if lt IE 9]>
    <script src="_static/js/html5shiv.min.js"></script>
  <![endif]-->
  
        <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
        <script src="_static/jquery.js"></script>
        <script src="_static/underscore.js"></script>
        <script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
        <script src="_static/doctools.js"></script>
    <script src="_static/js/theme.js"></script>
    <link rel="index" title="索引" href="genindex.html" />
    <link rel="search" title="搜索" href="search.html" />
    <link rel="next" title="Ch7-1 初识顶点缓冲区" href="Ch7-1%20%E5%88%9D%E8%AF%86%E9%A1%B6%E7%82%B9%E7%BC%93%E5%86%B2%E5%8C%BA.html" />
    <link rel="prev" title="Ch6-1 无图像帧缓冲" href="Ch6-1%20%E6%97%A0%E5%9B%BE%E5%83%8F%E5%B8%A7%E7%BC%93%E5%86%B2.html" /> 
</head>

<body class="wy-body-for-nav"> 
  <div class="wy-grid-for-nav">
    <nav data-toggle="wy-nav-shift" class="wy-nav-side">
      <div class="wy-side-scroll">
        <div class="wy-side-nav-search" >
            <a href="index.html" class="icon icon-home"> EasyVulkan
            <img src="_static/logo1.png" class="logo" alt="Logo"/>
          </a>
<div role="search">
  <form id="rtd-search-form" class="wy-form" action="search.html" method="get">
    <input type="text" name="q" placeholder="在文档中搜索" />
    <input type="hidden" name="check_keywords" value="yes" />
    <input type="hidden" name="area" value="default" />
  </form>
</div>
        </div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
              <p class="caption" role="heading"><span class="caption-text">第一章 初始化</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch1-0%20%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C.html">Ch1-0 准备工作</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch1-1%20%E5%88%9B%E5%BB%BAGLFW%E7%AA%97%E5%8F%A3.html">Ch1-1 创建GLFW窗口</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch1-2%20%E5%88%9D%E5%A7%8B%E5%8C%96%E6%B5%81%E7%A8%8B.html">Ch1-2 初始化流程</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch1-3%20%E5%88%9B%E5%BB%BAVK%E5%AE%9E%E4%BE%8B%E4%B8%8E%E9%80%BB%E8%BE%91%E8%AE%BE%E5%A4%87.html">Ch1-3 创建VK实例与逻辑设备</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch1-4%20%E5%88%9B%E5%BB%BA%E4%BA%A4%E6%8D%A2%E9%93%BE.html">Ch1-4 创建交换链</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第二章 绘制一个三角形</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch2-0%20%E4%BB%A3%E7%A0%81%E6%95%B4%E7%90%86%E5%8F%8A%E4%B8%80%E4%BA%9B%E8%BE%85%E5%8A%A9%E7%B1%BB.html">Ch2-0 代码整理及一些辅助类</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch2-1%20Rendering%20Loop.html">Ch2-1 Rendering Loop</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch2-2%20%E5%88%9B%E5%BB%BA%E6%B8%B2%E6%9F%93%E9%80%9A%E9%81%93%E5%92%8C%E5%B8%A7%E7%BC%93%E5%86%B2.html">Ch2-2 创建渲染通道和帧缓冲</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch2-3%20%E5%88%9B%E5%BB%BA%E7%AE%A1%E7%BA%BF%E5%B9%B6%E7%BB%98%E5%88%B6%E4%B8%89%E8%A7%92%E5%BD%A2.html">Ch2-3 创建管线并绘制三角形</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第三章 纵观Vulkan</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch3-1%20%E5%90%8C%E6%AD%A5%E5%8E%9F%E8%AF%AD.html">Ch3-1 同步原语</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-2%20%E5%9B%BE%E5%83%8F%E4%B8%8E%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch3-2 图像与缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-3%20%E7%AE%A1%E7%BA%BF%E5%B8%83%E5%B1%80%E5%92%8C%E7%AE%A1%E7%BA%BF.html">Ch3-3 管线布局和管线</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-4%20%E6%B8%B2%E6%9F%93%E9%80%9A%E9%81%93%E5%92%8C%E5%B8%A7%E7%BC%93%E5%86%B2.html">Ch3-4 渲染通道和帧缓冲</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-5%20%E5%91%BD%E4%BB%A4%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch3-5 命令缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-6%20%E6%8F%8F%E8%BF%B0%E7%AC%A6.html">Ch3-6 描述符</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-7%20%E9%87%87%E6%A0%B7%E5%99%A8.html">Ch3-7 采样器</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-8%20%E6%9F%A5%E8%AF%A2.html">Ch3-8 查询</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第四章 着色器</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch4-1%20%E7%9D%80%E8%89%B2%E5%99%A8%E6%A8%A1%E7%BB%84.html">Ch4-1 着色器模组</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch4-2%20%E9%A1%B6%E7%82%B9%E7%9D%80%E8%89%B2%E5%99%A8.html">Ch4-2 顶点着色器</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch4-3%20%E7%89%87%E6%AE%B5%E7%9D%80%E8%89%B2%E5%99%A8.html">Ch4-3 片段着色器</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch4-4%20%E5%87%A0%E4%BD%95%E7%9D%80%E8%89%B2%E5%99%A8.html">Ch4-4 几何着色器</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第五章 封装常用对象</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch5-0%20VKBase%2B.h.html">Ch5-0 VKBase+.h</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch5-1%20%E5%90%84%E7%A7%8D%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch5-1 各种缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch5-2%202D%E8%B4%B4%E5%9B%BE%E5%8F%8A%E7%94%9F%E6%88%90Mipmap.html">Ch5-2 2D贴图及生成Mipmap</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch5-3%202D%E8%B4%B4%E5%9B%BE%E6%95%B0%E7%BB%84.html">Ch5-3 2D贴图数组</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第六章 进阶Vulkan</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="Ch6-0%20%E4%BD%BF%E7%94%A8%E6%96%B0%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7.html">Ch6-0 使用新版本特性</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch6-1%20%E6%97%A0%E5%9B%BE%E5%83%8F%E5%B8%A7%E7%BC%93%E5%86%B2.html">Ch6-1 无图像帧缓冲</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">Ch6-2 动态渲染</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#id1">开启动态渲染设备特性</a></li>
<li class="toctree-l2"><a class="reference internal" href="#id2">更改管线创建信息</a></li>
<li class="toctree-l2"><a class="reference internal" href="#id3">录制渲染前后的内存屏障</a></li>
<li class="toctree-l2"><a class="reference internal" href="#id4">录制动态渲染命令</a></li>
<li class="toctree-l2"><a class="reference internal" href="#id5">多线程动态渲染</a></li>
</ul>
</li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第七章 基础示例</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch7-1%20%E5%88%9D%E8%AF%86%E9%A1%B6%E7%82%B9%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch7-1 初识顶点缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-2%20%E5%88%9D%E8%AF%86%E7%B4%A2%E5%BC%95%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch7-2 初识索引缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-3%20%E5%88%9D%E8%AF%86%E5%AE%9E%E4%BE%8B%E5%8C%96%E7%BB%98%E5%88%B6.html">Ch7-3 初识实例化绘制</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-4%20%E5%88%9D%E8%AF%86Push%20Constant.html">Ch7-4 初识Push Constant</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-5%20%E5%88%9D%E8%AF%86Uniform%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch7-5 初识Uniform缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-6%20%E6%8B%B7%E8%B4%9D%E5%9B%BE%E5%83%8F%E5%88%B0%E5%B1%8F%E5%B9%95.html">Ch7-6 拷贝图像到屏幕</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-7%20%E4%BD%BF%E7%94%A8%E8%B4%B4%E5%9B%BE.html">Ch7-7 使用贴图</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第八章 简单示例</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch8-1%20%E7%A6%BB%E5%B1%8F%E6%B8%B2%E6%9F%93.html">Ch8-1 离屏渲染</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch8-2%20%E6%B7%B1%E5%BA%A6%E6%B5%8B%E8%AF%95%E5%92%8C%E6%B7%B1%E5%BA%A6%E5%8F%AF%E8%A7%86%E5%8C%96.html">Ch8-2 深度测试和深度可视化</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch8-3%20%E5%BB%B6%E8%BF%9F%E6%B8%B2%E6%9F%93.html">Ch8-3 延迟渲染</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch8-4%20%E9%A2%84%E4%B9%98Alpha.html">Ch8-4 预乘Alpha</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch8-5%20sRGB%E8%89%B2%E5%BD%A9%E7%A9%BA%E9%97%B4%E4%B8%8E%E5%BC%80%E5%90%AFHDR.html">Ch8-5 sRGB色彩空间与开启HDR</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">附录</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ap1-1%20%E8%BF%90%E8%A1%8C%E6%9C%9F%E7%BC%96%E8%AF%91GLSL.html">Ap1-1 运行期编译GLSL</a></li>
</ul>

        </div>
      </div>
    </nav>

    <section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
          <i data-toggle="wy-nav-top" class="fa fa-bars"></i>
          <a href="index.html">EasyVulkan</a>
      </nav>

      <div class="wy-nav-content">
        <div class="rst-content">
          <div role="navigation" aria-label="Page navigation">
  <ul class="wy-breadcrumbs">
      <li><a href="index.html" class="icon icon-home"></a> &raquo;</li>
      <li>Ch6-2 动态渲染</li>
      <li class="wy-breadcrumbs-aside">
      </li>
  </ul>
  <hr/>
</div>
          <div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
           <div itemprop="articleBody">
             
  <section id="ch6-2">
<h1>Ch6-2 动态渲染<a class="headerlink" href="#ch6-2" title="Permalink to this heading"></a></h1>
<p>
    动态渲染（dynamic rendering），简而言之是让渲染通道对象和帧缓冲一起滚蛋的渲染方式。
    <br>
    所谓动态，指渲染相关的参数大都在渲染开始时指定。
</p>
<p>
    不需要渲染通道对象和帧缓冲，意味着省去了对它们进行管理的麻烦！尤其是，不再需要“因窗口大小改变而在重建交换链后重建渲染通道和帧缓冲”。
    <br>
    渲染通道对象在子通道开始和结束时会执行子通道依赖，并进行图像内存布局转换。而在动态渲染中，原先子通道依赖的作用和图像内存布局转换得通过<a class="reference internal" href="Ch3-1%20%E5%90%8C%E6%AD%A5%E5%8E%9F%E8%AF%AD.html#pipeline-barrier">图像内存屏障</a>来实行。
</p>
<p>
    动态渲染相比渲染通道对象有一个显著劣势：不能使用输入附件。而输入附件的设备内存往往可以被指定为<a class="reference internal" href="Ch3-2%20%E5%9B%BE%E5%83%8F%E4%B8%8E%E7%BC%93%E5%86%B2%E5%8C%BA.html#id18">惰性分配</a>以优化内存开销。
</p>
<div class="admonition note">
    <p class="admonition-title">Note</p>
    <p>
        <strong>渲染通道的定义</strong>
        <br>
        留意到上文我使用了渲染通道对象（render pass object）这个词，而非直接说渲染通道。
        <br>
        Vulkan标准中将动态渲染也定义为一种渲染通道实例。
    </p>
</div>
<p>
    本节在之前Ch2中绘制三角形代码的基础上，使用动态渲染的方法来进行绘制，步骤如下：
    <br>
    1.开启设备特性
    <br>
    2.更改管线创建信息
    <br>
    3.定义动态渲染前后的内存屏障
    <br>
    4.调用命令，开始/结束动态渲染
</p><section id="id1">
<h2>开启动态渲染设备特性<a class="headerlink" href="#id1" title="Permalink to this heading"></a></h2>
<p>
    动态渲染是在Vulkan1.2.203版本中引入的功能，Vulkan1.3版本之前由设备级扩展<span class="str">&quot;VK_KHR_dynamic_rendering&quot;</span>提供，从1.3版本开始为Vulkan的核心功能。
</p>
<p>
    取得Vulkan在本机上可用的最高版本，只有1.0.XXX或1.1.XXX的话直接返回。
    <br>
    Vulkan版本达到1.3的话，设备特性已在<span class="type">graphicsBase</span>::<span class="fn">CreateDevice</span>(...)中“能开尽开”，主函数中只检查设备特性是否支持：
</p>
<pre class="code">
<span class="kw">int</span> <span class="fn">main</span>() {
    <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">UseLatestApiVersion</span>();
    <span class="kw">if</span> (<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">ApiVersion</span>() &lt; <span class="mcr">VK_API_VERSION_1_2</span>)
        <span class="kw">return</span> -1;
    <span class="kw">if</span> (<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">ApiVersion</span>() &lt; <span class="mcr">VK_API_VERSION_1_3</span>) {
        <span class="cmt">/*待填充*/</span>
    }
    <span class="kw">else</span>
        <span class="kw">if</span> (!<span class="fn">InitializeWindow</span>({ 1280, 720 }) ||
            !<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">PhysicalDeviceVulkan13Features</span>().dynamicRendering)
            <span class="kw">return</span> -1;

    <span class="cmt">/*...*/</span>
}
</pre>
<p>
    若Vulkan版本达到1.2但未到1.3，使用以下代码通过扩展开启：
</p>
<pre class="code">
<span class="kw">int</span> <span class="fn">main</span>() {
    <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">UseLatestApiVersion</span>();
    <span class="kw">if</span> (<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">ApiVersion</span>() &lt; <span class="mcr">VK_API_VERSION_1_2</span>)
        <span class="kw">return</span> -1;
    <span class="kw">if</span> (<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">ApiVersion</span>() &lt; <span class="mcr">VK_API_VERSION_1_3</span>) {
        <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">AddDeviceExtension</span>(<span class="mcr">VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME</span>);
        <span class="type">VkPhysicalDeviceDynamicRenderingFeatures</span> physicalDeviceDynamicRenderingFeatures = {
            <span class="enum">VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES</span>,
        };
        <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">AddNextStructure_PhysicalDeviceFeatures</span>(physicalDeviceDynamicRenderingFeatures);
        <span class="kw">if</span> (!<span class="fn">InitializeWindow</span>({ 1280, 720 }) ||
            !physicalDeviceDynamicRenderingFeatures.dynamicRendering)
            <span class="kw">return</span> -1;
    }
    <span class="kw">else</span>
        <span class="kw">if</span> (!<span class="fn">InitializeWindow</span>({ 1280, 720 }) ||
            !<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">PhysicalDeviceVulkan13Features</span>().dynamicRendering)
            <span class="kw">return</span> -1;

    <span class="cmt">/*...*/</span>
}
</pre></section>
<section id="id2">
<h2>更改管线创建信息<a class="headerlink" href="#id2" title="Permalink to this heading"></a></h2>
<p>
    为什么要改管线创建信息？先来看看原先的管线创建信息：
</p>
<pre class="code">
<span class="type">graphicsPipelineCreateInfoPack</span> pipelineCiPack;
pipelineCiPack.createInfo.layout = pipelineLayout_triangle;
pipelineCiPack.createInfo.renderPass = <span class="fn">RenderPassAndFramebuffers</span>().renderPass;
pipelineCiPack.inputAssemblyStateCi.topology = <span class="enum">VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST</span>;
pipelineCiPack.viewports.<span class="fn">emplace_back</span>(0.f, 0.f, <span class="kw">float</span>(windowSize.width), <span class="kw">float</span>(windowSize.height), 0.f, 1.f);
pipelineCiPack.scissors.<span class="fn">emplace_back</span>(<span class="type">VkOffset2D</span>{}, windowSize);
pipelineCiPack.multisampleStateCi.rasterizationSamples = <span class="enum">VK_SAMPLE_COUNT_1_BIT</span>;
pipelineCiPack.colorBlendAttachmentStates.<span class="fn">push_back</span>({ .colorWriteMask = 0b1111 });
pipelineCiPack.<span class="fn">UpdateAllArrays</span>();
pipelineCiPack.createInfo.stageCount = 2;
pipelineCiPack.createInfo.pStages = shaderStageCreateInfos_triangle;
</pre>
<p>
    这里有个渲染通道对象在，而动态渲染中不用渲染通道对象，相应地在<span class="type">VkGraphicsPipelineCreateInfo</span>的pNext链中包含一个<span class="type">VkPipelineRenderingCreateInfoKHR</span>结构体：
</p>
<table class="docutils align-default">
    <colgroup>
        <col style="width: 30%">
        <col style="width: 70%">
    </colgroup>
    <thead>
        <tr class="row-odd">
            <th class="head" colspan="2"><p><span class="kw">struct</span> <a href="https://renderdoc.org/vkspec_chunked/chap10.html#VkPipelineRenderingCreateInfo">VkPipelineRenderingCreateInfo</a> 的成员说明</p></th>
        </tr>
    </thead>
    <tbody>
        <tr class="row-even">
            <td><p><span class="type">VkStructureType</span> sType</p></td>
            <td><p>结构体的类型，本处必须是<span class="enum">VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2</span></p></td>
        </tr>
        <tr class="row-odd">
            <td><p><span class="kw">const void</span>* pNext</p></td>
            <td><p>如有必要，指向一个用于扩展该结构体的结构体</p></td>
        </tr>
        <tr class="row-even">
            <td><p><span class="type">uint32_t</span> viewMask</p></td>
            <td><p>若开启了多视点（multiview），指定在哪些视点对应的图层上进行渲染，各个bit对应多层帧缓冲相应索引的图层</p></td>
        </tr>
        <tr class="row-odd">
            <td><p><span class="type">uint32_t</span> colorAttachmentCount</p></td>
            <td><p>颜色附件的数量</p></td>
        </tr>
        <tr class="row-even">
            <td><p><span class="kw">const</span> <span class="type">VkFormat</span>* pColorAttachmentFormats</p></td>
            <td><p>指向<span class="type">VkFormat</span>的数组，用于描述届时各个颜色附件的格式</p></td>
        </tr>
        <tr class="row-odd">
            <td><p><span class="type">VkFormat</span> depthAttachmentFormat</p></td>
            <td><p>深度附件的格式，若届时不使用深度附件，留作<span class="enum">VK_FORMAT_UNDEFINED</span>即可</p></td>
        </tr>
        <tr class="row-even">
            <td><p><span class="type">VkFormat</span> stencilAttachmentFormat</p></td>
            <td><p>模板附件的格式，若届时不使用模板附件，留作<span class="enum">VK_FORMAT_UNDEFINED</span>即可</p></td>
        </tr>
    </tbody>
</table>
<ul>
    <li>
        <p>
            这里允许使用两张不同的图像各自作为深度/模板附件。
        </p>
    </li>
</ul>
<p>
    颜色附件的数量为1，格式可从交换链创建信息取得。
    <br>
    不使用深度或模板附件，<span class="enum">VK_FORMAT_UNDEFINED</span>即0，于是：
</p>
<pre class="code">
<span class="type">VkPipelineRenderingCreateInfoKHR</span> pipelineRenderingCreateInfo = {
    .sType = <span class="enum">VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO</span>,
    .colorAttachmentCount = 1,
    .pColorAttachmentFormats = &amp;<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">SwapchainCreateInfo</span>().imageFormat
};
<span class="type">graphicsPipelineCreateInfoPack</span> pipelineCiPack;
pipelineCiPack.createInfo.pNext = &amp;pipelineRenderingCreateInfo;
pipelineCiPack.createInfo.layout = pipelineLayout_triangle;
<span class="cmt">//pipelineCiPack.createInfo.renderPass = RenderPassAndFramebuffers().renderPass;</span>
pipelineCiPack.inputAssemblyStateCi.topology = <span class="enum">VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST</span>;
pipelineCiPack.viewports.<span class="fn">emplace_back</span>(0.f, 0.f, <span class="kw">float</span>(windowSize.width), <span class="kw">float</span>(windowSize.height), 0.f, 1.f);
pipelineCiPack.scissors.<span class="fn">emplace_back</span>(<span class="type">VkOffset2D</span>{}, windowSize);
pipelineCiPack.multisampleStateCi.rasterizationSamples = <span class="enum">VK_SAMPLE_COUNT_1_BIT</span>;
pipelineCiPack.colorBlendAttachmentStates.<span class="fn">push_back</span>({ .colorWriteMask = 0b1111 });
pipelineCiPack.<span class="fn">UpdateAllArrays</span>();
pipelineCiPack.createInfo.stageCount = 2;
pipelineCiPack.createInfo.pStages = shaderStageCreateInfos_triangle;
</pre></section>
<section id="id3">
<h2>录制渲染前后的内存屏障<a class="headerlink" href="#id3" title="Permalink to this heading"></a></h2>
<p>
    这一步要做的，就是把子通道依赖和渲染通道中图像内存布局的变化，改写成<a class="reference internal" href="Ch3-1%20%E5%90%8C%E6%AD%A5%E5%8E%9F%E8%AF%AD.html#pipeline-barrier">图像内存屏障</a>。往下阅读之前，请确保已经明白了内存屏障的使用方法和作用（如果真的明白了的话，下面的反倒全成了复习+废话）。
</p>
<p>
   先看看图像内存屏障有哪些参数要填：
</p>
<pre class="code">
commandBuffer.<span class="fn">Begin</span>(<span class="enum">VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT</span>);
<span class="cmt">//渲染开始前的内存屏障</span>
<span class="type">VkImageMemoryBarrier</span> imageMemoryBarrier = {
    .sType = <span class="enum">VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER</span>,
    .pNext = <span class="kw">nullptr</span>,
    .srcAccessMask = <span class="cmt">/*待填充*/</span>,
    .dstAccessMask = <span class="cmt">/*待填充*/</span>,
    .oldLayout = <span class="cmt">/*待填充*/</span>,
    .newLayout = <span class="cmt">/*待填充*/</span>,
    .srcQueueFamilyIndex = <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
    .dstQueueFamilyIndex = <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
    .image = <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">SwapchainImage</span>(i),
    .subresourceRange = { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 1, 0, 1 }
};
<span class="fn">vkCmdPipelineBarrier</span>(
    commandBuffer,
    <span class="cmt">/*srcStageMask*/</span>,
    <span class="cmt">/*dstStageMask*/</span>,
    <span class="cmt">/*dependencyFlags*/</span>,
    0, <span class="kw">nullptr</span>,
    0, <span class="kw">nullptr</span>,
    1, &amp;imageMemoryBarrier);

<span class="cmt">/*渲染，待填充*/</span>

<span class="cmt">//渲染结束后的内存屏障</span>
imageMemoryBarrier.srcAccessMask = <span class="cmt">/*待填充*/</span>;
imageMemoryBarrier.dstAccessMask = <span class="cmt">/*待填充*/</span>;
imageMemoryBarrier.oldLayout = <span class="cmt">/*待填充*/</span>;
imageMemoryBarrier.newLayout = <span class="cmt">/*待填充*/</span>;
<span class="fn">vkCmdPipelineBarrier</span>(
    commandBuffer,
    <span class="cmt">/*srcStageMask*/</span>,
    <span class="cmt">/*dstStageMask*/</span>,
    <span class="cmt">/*dependencyFlags*/</span>,
    0, <span class="kw">nullptr</span>,
    0, <span class="kw">nullptr</span>,
    1, &amp;imageMemoryBarrier);
commandBuffer.<span class="fn">End</span>();
</pre>
<p>
    先前创建渲染通道对象时，唯一的子通道开始时的依赖是这样的：
</p>
<pre class="code">
<span class="type">VkSubpassDependency</span> subpassDependency = {
    .srcSubpass = <span class="mcr">VK_SUBPASS_EXTERNAL</span>,
    .dstSubpass = 0,
    .srcStageMask = <span class="enum">VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT</span>,
    .dstStageMask = <span class="enum">VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT</span>,
    .srcAccessMask = 0,
    .dstAccessMask = <span class="enum">VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT</span>,
    .dependencyFlags = <span class="enum">VK_DEPENDENCY_BY_REGION_BIT</span>
};
</pre>
<ul>
    <li>
        <p>
            前情回顾：这里这个<code>.srcStageMask = <span class="enum">VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT</span></code>只是当初出于演示而这么写的，因为有其他粒度更粗的同步手段加之前面没有任何要阻塞的其他命令，那么实际上srcStageMask是啥都行。
        </p>
    </li>
</ul>
<p>
    srcStageMask按常规做法指定为<span class="enum">VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT</span>，其余各项对应的直接照抄。
    <br>
    图像内存布局从<span class="enum">VK_IMAGE_LAYOUT_UNDEFINED</span>变为最适合渲染的<span class="enum">VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL</span>：
</p>
<pre class="code">
<span class="type">VkImageMemoryBarrier</span> imageMemoryBarrier = {
    .sType = <span class="enum">VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER</span>,
    .srcAccessMask = 0,
    .dstAccessMask = <span class="enum">VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT</span>,
    .oldLayout = <span class="enum">VK_IMAGE_LAYOUT_UNDEFINED</span>,
    .newLayout = <span class="enum">VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL</span>,
    .srcQueueFamilyIndex = <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
    .dstQueueFamilyIndex = <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
    .image = <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">SwapchainImage</span>(i),
    .subresourceRange = { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 1, 0, 1 }
};
<span class="fn">vkCmdPipelineBarrier</span>(
    commandBuffer,
    <span class="enum">VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT</span>,
    <span class="enum">VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT</span>,
    <span class="enum">VK_DEPENDENCY_BY_REGION_BIT</span>,
    0, <span class="kw">nullptr</span>,
    0, <span class="kw">nullptr</span>,
    1, &amp;imageMemoryBarrier);
</pre>
<p>
    渲染结束后没有其他命令，命令提交后到呈现为止使用的是信号量来同步，所以渲染后图像内存屏障的dstAccessMask是0，dstStageMask按常规做法指定为<span class="enum">VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT</span>。
    <br>
    图像内存布局转到最适合呈现的<span class="enum">VK_IMAGE_LAYOUT_PRESENT_SRC_KHR</span>：
</p>
<pre class="code">
<span class="type">VkImageMemoryBarrier</span> imageMemoryBarrier = {
    .sType = <span class="enum">VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER</span>,
    .srcAccessMask = 0,
    .dstAccessMask = <span class="enum">VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT</span>,
    .oldLayout = <span class="enum">VK_IMAGE_LAYOUT_UNDEFINED</span>,
    .newLayout = <span class="enum">VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL</span>,
    .srcQueueFamilyIndex = <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
    .dstQueueFamilyIndex = <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
    .image = <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">SwapchainImage</span>(i),
    .subresourceRange = { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 1, 0, 1 }
};
<span class="fn">vkCmdPipelineBarrier</span>(
    commandBuffer,
    <span class="enum">VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT</span>,
    <span class="enum">VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT</span>,
    <span class="enum">VK_DEPENDENCY_BY_REGION_BIT</span>,
    0, <span class="kw">nullptr</span>,
    0, <span class="kw">nullptr</span>,
    1, &amp;imageMemoryBarrier);

<span class="cmt">/*渲染，待填充*/</span>

imageMemoryBarrier.srcAccessMask = <span class="enum">VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT</span>;
imageMemoryBarrier.dstAccessMask = 0;
imageMemoryBarrier.oldLayout = <span class="enum">VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL</span>;
imageMemoryBarrier.newLayout = <span class="enum">VK_IMAGE_LAYOUT_PRESENT_SRC_KHR</span>;
<span class="fn">vkCmdPipelineBarrier</span>(
    commandBuffer,
    <span class="enum">VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT</span>,
    <span class="enum">VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT</span>,
    <span class="enum">VK_DEPENDENCY_BY_REGION_BIT</span>,
    0, <span class="kw">nullptr</span>,
    0, <span class="kw">nullptr</span>,
    1, &amp;imageMemoryBarrier);
commandBuffer.<span class="fn">End</span>();
</pre></section>
<section id="id4">
<h2>录制动态渲染命令<a class="headerlink" href="#id4" title="Permalink to this heading"></a></h2>
<p>
    用<span class="fn">vkCmdBeginRendering</span>(...)开始渲染，用<span class="fn">vkCmdEndRendering</span>(...)结束渲染。
    <br>
    先等等，在这之前还有个问题（不关心Vulkan版本的话请直接下拉到函数说明）。
</p>
<p>
    你用的Vulkan SDK肯定是1.3版本以上，所以必定定义了这两个符号，在Vulkan1.3中也不需要干任何额外的事。
    <br>
    但是，如果Vulkan的运行版本是1.2，这两个函数不是核心功能的一部分，那么程序是无法在运行期找到这两个函数的实现的。跟<span class="type">PFN_vkCreateDebugUtilsMessengerEXT</span>的情况类似，需要手动获取这两个函数的指针，因为他们从属于设备扩展功能，通过<a href="https://renderdoc.org/vkspec_chunked/chap4.html#vkGetDeviceProcAddr">vkGetDeviceProcAddr</a>(...)来取得：
</p>
<pre class="code">
<span class="type">PFN_vkCmdBeginRenderingKHR</span> vkCmdBeginRendering =
    <span class="kw">reinterpret_cast</span>&lt;<span class="type">PFN_vkCmdBeginRenderingKHR</span>&gt;(<span class="fn">vkGetDeviceProcAddr</span>(<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">Device</span>(), <span class="str">&quot;vkCmdBeginRenderingKHR&quot;</span>));
<span class="type">PFN_vkCmdEndRenderingKHR</span> vkCmdEndRendering =
    <span class="kw">reinterpret_cast</span>&lt;<span class="type">PFN_vkCmdEndRenderingKHR</span>&gt;(<span class="fn">vkGetDeviceProcAddr</span>(<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">Device</span>(), <span class="str">&quot;vkCmdEndRenderingKHR&quot;</span>));
</pre>
<div class="admonition note">
    <p class="admonition-title">Note</p>
    <p>
        使用<a href="https://renderdoc.org/vkspec_chunked/chap4.html#vkGetInstanceProcAddr">vkGetInstanceProcAddr</a>(...)也可以取得设备级扩展功能的函数指针，但<a href="https://renderdoc.org/vkspec_chunked/chap4.html#vkGetDeviceProcAddr">vkGetDeviceProcAddr</a>(...)所取得的会是设备特定的版本，而前者所取得的则是一个间接地调用后者结果的函数（即会有一个分发给特定设备的过程）。
    </p>
</div>
<p>
    考虑同时兼容Vulkan1.2和1.3的代码：
</p>
<pre class="code">
<span class="kw">int</span> <span class="fn">main</span>() {
    <span class="type">PFN_vkCmdBeginRenderingKHR</span> vkCmdBeginRendering = ::<span class="fn">vkCmdBeginRendering</span>;
    <span class="type">PFN_vkCmdEndRenderingKHR</span> vkCmdEndRendering = ::<span class="fn">vkCmdEndRendering</span>;

    <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">UseLatestApiVersion</span>();
    <span class="kw">if</span> (<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">ApiVersion</span>() &lt; <span class="mcr">VK_API_VERSION_1_2</span>)
        <span class="kw">return</span> -1;
    <span class="kw">if</span> (<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">ApiVersion</span>() &lt; <span class="mcr">VK_API_VERSION_1_3</span>) {
        <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">AddDeviceExtension</span>(<span class="mcr">VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME</span>);
        <span class="type">VkPhysicalDeviceDynamicRenderingFeatures</span> physicalDeviceDynamicRenderingFeatures = {
            <span class="enum">VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES</span>,
        };
        <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">AddNextStructure_PhysicalDeviceFeatures</span>(physicalDeviceDynamicRenderingFeatures);
        <span class="kw">if</span> (!<span class="fn">InitializeWindow</span>({ 1280, 720 }) ||
            !physicalDeviceDynamicRenderingFeatures.dynamicRendering)
            <span class="kw">return</span> -1;
        vkCmdBeginRendering = <span class="kw">reinterpret_cast</span>&lt;<span class="type">PFN_vkCmdBeginRenderingKHR</span>&gt;(<span class="fn">vkGetDeviceProcAddr</span>(<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">Device</span>(), <span class="str">&quot;vkCmdBeginRenderingKHR&quot;</span>));
        vkCmdEndRendering = <span class="kw">reinterpret_cast</span>&lt;<span class="type">PFN_vkCmdEndRenderingKHR</span>&gt;(<span class="fn">vkGetDeviceProcAddr</span>(<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">Device</span>(), <span class="str">&quot;vkCmdEndRenderingKHR&quot;</span>));
    }
    <span class="kw">else</span>
        <span class="kw">if</span> (!<span class="fn">InitializeWindow</span>({ 1280, 720 }) ||
            !<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">PhysicalDeviceVulkan13Features</span>().dynamicRendering)
            <span class="kw">return</span> -1;

    <span class="cmt">/*...*/</span>
}
</pre>
<ul>
    <li>
        <p>
            ::前无名称表示全局命名空间，可用来消歧义。
        </p>
    </li>
</ul>
<p>
    调用<span class="fn">vkCmdBeginRendering</span>(...)和<span class="fn">vkCmdEndRendering</span>(...)，俩函数本身的参数列表看以下代码应该一眼就能懂：
</p>
<pre class="code">
<span class="type">VkRenderingInfo</span> renderingInfo = {
    .sType = <span class="enum">VK_STRUCTURE_TYPE_RENDERING_INFO</span>,
    <span class="enum">/*待填充*/</span>
};
vkCmdBeginRendering(commandBuffer, &amp;renderingInfo);
</span><span class="fn">vkCmdBindPipeline</span>(commandBuffer, <span class="enum">VK_PIPELINE_BIND_POINT_GRAPHICS</span>, pipeline_triangle);
</span><span class="fn">vkCmdDraw</span>(commandBuffer, 3, 1, 0, 0);
vkCmdEndRendering(commandBuffer);
</pre>
<table class="docutils align-default">
    <colgroup>
        <col style="width: 30%">
        <col style="width: 70%">
    </colgroup>
    <thead>
        <tr class="row-odd">
            <th class="head" colspan="2"><p><span class="kw">struct</span> <a href="https://renderdoc.org/vkspec_chunked/chap8.html#VkRenderingInfo">VkRenderingInfo</a> 的成员说明</p></th>
        </tr>
    </thead>
    <tbody>
        <tr class="row-even">
            <td><p><span class="type">VkStructureType</span> sType</p></td>
            <td><p>结构体的类型，本处必须是<span class="enum">VK_STRUCTURE_TYPE_RENDERING_INFO</span></p></td>
        </tr>
        <tr class="row-odd">
            <td><p><span class="kw">const void</span>* pNext</p></td>
            <td><p>如有必要，指向一个用于扩展该结构体的结构体</p></td>
        </tr>
        <tr class="row-even">
            <td><p><span class="type">VkRenderingFlags</span> flags</p></td>
            <td><p></p></td>
        </tr>
        <tr class="row-odd">
            <td><p><span class="type">VkRect2D</span> renderArea</p></td>
            <td><p>渲染区域</p></td>
        </tr>
        <tr class="row-even">
            <td><p><span class="type">uint32_t</span> layerCount</p></td>
            <td><p>帧缓冲的图层数，若viewMask非0，该项被无视</p></td>
        </tr>
        <tr class="row-odd">
            <td><p><span class="type">uint32_t</span> viewMask</p></td>
            <td><p>若开启了多视点（multiview），指定在哪些视点对应的图层上进行渲染，各个bit对应多层帧缓冲相应索引的图层</p></td>
        </tr>
        <tr class="row-even">
            <td><p><span class="type">uint32_t</span> colorAttachmentCount</p></td>
            <td><p>颜色附件的数量</p></td>
        </tr>
        <tr class="row-odd">
            <td><p><span class="kw">const</span> <span class="type">VkRenderingAttachmentInfo</span>* pColorAttachments</p></td>
            <td><p>指向<span class="type">VkRenderingAttachmentInfo</span>的数组，用于指定各个颜色附件及相关参数</p></td>
        </tr>
        <tr class="row-even">
            <td><p><span class="kw">const</span> <span class="type">VkRenderingAttachmentInfo</span>* pDepthAttachment</p></td>
            <td><p>指向<span class="type">VkRenderingAttachmentInfo</span>结构体，用于指定深度附件及相关参数</p></td>
        </tr>
        <tr class="row-odd">
            <td><p><span class="kw">const</span> <span class="type">VkRenderingAttachmentInfo</span>* pStencilAttachment</p></td>
            <td><p>指向<span class="type">VkRenderingAttachmentInfo</span>结构体，用于指定模板附件及相关参数</p></td>
        </tr>
    </tbody>
</table>
    <table class="docutils align-default">
    <colgroup>
        <col>
        <col>
    </colgroup>
    <thead>
        <tr class="row-odd">
            <th class="head"><p>版本要求</p></th>
            <th class="head"><p><a href="https://renderdoc.org/vkspec_chunked/chap8.html#VkRenderingFlagBits">VkRenderingFlagBits</a> 的枚举项</p></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><p>1.3</p></td>
            <td><p><span class="enum">VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT </span>表示<strong>可以</strong>调用<span class="fn">vkCmdExecuteCommands</span>(...)执行二级命令缓冲区</p></td>
        </tr>
        <tr>
            <td><p>1.3</p></td>
            <td><p><span class="enum">VK_RENDERING_SUSPENDING_BIT                         </span>表示下一次<span class="fn">vkCmdEndRendering</span>(...)会将当前渲染通道挂起</p></td>
        </tr>
        <tr>
            <td><p>1.3</p></td>
            <td><p><span class="enum">VK_RENDERING_RESUMING_BIT                           </span>表示本次<span class="fn">vkCmdBeginRendering</span>(...)恢复挂起的渲染通道</p></td>
        </tr>
    </tbody>
</table>
<ul>
    <li>
        <p>
            <span class="enum">VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT</span>跟<span class="enum">VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS</span>不同，后者使得子通道中<strong>只能</strong>执行二级缓冲区。
        </p>
    </li>
    <li>
        <p>
            挂起和恢复可以分别录制在不同的命令缓冲区中，但这些命令缓冲区必须在同一批次中提交，提交顺序必须使得一个渲染通道首次开始时只挂起，最后一次时只恢复。
            <br>
            <strong>这意味着你可以无序录制命令（在不同CPU线程中录制，或先录制“恢复”，再录制“挂起”）！</strong>只要最后命令提交顺序使得命令执行顺序合乎逻辑即可。这在某种程度上可以取代“在其他线程中录制二级命令缓冲区，然后在主线程中让一级调用”（以节省命令录制时间）的做法。
        </p>
    </li>
    <li>
        <p>
            恢复被挂起的渲染通道时，<span class="type">VkRenderingInfo</span>的各成员除flags外，以及图像附件的诸多参数，必须与按命令提交顺序，前一个注明<span class="enum">VK_RENDERING_SUSPENDING_BIT</span>的<span class="fn">vkCmdBeginRendering</span>(...)命令相同。
        </p>
    </li>
    <li>
        <p>
            提示：恢复时当然可以同时注明<span class="enum">VK_RENDERING_SUSPENDING_BIT</span>和<span class="enum">VK_RENDERING_RESUMING_BIT</span>，表示之后还会挂起。
        </p>
    </li>
    <li>
        <p>
            从挂起到恢复之间（指命令执行先后，而非录制先后），不能有绘制、传输等动作命令，不能有同步命令（这使得绑定管线等状态命令是唯一可行的命令），不能开始其他的渲染通道。
            <br>
            挂起时不会应用storeOp、内存布局转换等渲染结束时的选项或操作，恢复时同理。
        </p>
    </li>
</ul>
<table class="docutils align-default">
    <colgroup>
        <col style="width: 30%">
        <col style="width: 70%">
    </colgroup>
    <thead>
        <tr class="row-odd">
            <th class="head" colspan="2"><p><span class="kw">struct</span> <a href="https://renderdoc.org/vkspec_chunked/chap8.html#VkRenderingAttachmentInfo">VkRenderingAttachmentInfo</a> 的成员说明</p></th>
        </tr>
    </thead>
    <tbody>
        <tr class="row-even">
            <td><p><span class="type">VkStructureType</span> sType</p></td>
            <td><p>结构体的类型，本处必须是<span class="enum">VK_STRUCTURE_TYPE_RENDERING_INFO</span></p></td>
        </tr>
        <tr class="row-odd">
            <td><p><span class="kw">const void</span>* pNext</p></td>
            <td><p>如有必要，指向一个用于扩展该结构体的结构体</p></td>
        </tr>
        <tr class="row-even">
            <td><p><span class="type">VkImageView</span> imageView</p></td>
            <td><p>被用于渲染的图像附件的图像视图</p></td>
        </tr>
        <tr class="row-odd">
            <td><p><span class="type">VkImageLayout</span> imageLayout</p></td>
            <td><p>被用于渲染的图像附件的内存布局</p></td>
        </tr>
        <tr class="row-even">
            <td><p><span class="type">VkResolveModeFlagBits</span> resolveMode</p></td>
            <td><p>如果进行多重采样，在这里指定解析模式</p></td>
        </tr>
        <tr class="row-odd">
            <td><p><span class="type">VkImageView</span> resolveImageView</p></td>
            <td><p>如果进行多重采样，在这里指定解析附件</p></td>
        </tr>
        <tr class="row-even">
            <td><p><span class="type">VkImageLayout</span> resolveImageLayout</p></td>
            <td><p>如果进行多重采样，在这里指定解析附件的内存布局</p></td>
        </tr>
        <tr class="row-odd">
            <td><p><span class="type">VkAttachmentLoadOp</span> loadOp</p></td>
            <td><p>读取imageView对应的图像附件时进行的操作</p></td>
        </tr>
        <tr class="row-even">
            <td><p><span class="type">VkAttachmentStoreOp</span> storeOp</p></td>
            <td><p>存储值到imageView对应的图像附件时的操作，或指定不在乎存储</p></td>
        </tr>
        <tr class="row-odd">
            <td><p><span class="type">VkClearValue</span> clearValue</p></td>
            <td><p>清屏值，用于loadOp为<span class="enum">VK_ATTACHMENT_LOAD_OP_CLEAR</span>的情况</p></td>
        </tr>
    </tbody>
</table>
<ul>
    <li>
        <p>
            resolveMode是个创建通常的渲染通道对象时没有的参数，但因为本节的重点不是多重渲染，在此不对<a href="https://renderdoc.org/vkspec_chunked/chap8.html#VkResolveModeFlagBits">VkResolveModeFlagBits</a>进行解说（注：这套教程中预计不会对解析模式进行解说，有兴趣请自行了解）。
        </p>
    </li>
    <li>
        <p>
            如果resolveMode和resolveImageView都非0，imageView的底层图像被解析到resolveImageView的底层图像。storeOp不影响解析。
        </p>
    </li>
</ul>
<p>
    对于仅仅是画个三角形这种情况，应该是没有什么难点需要解说的：
</p>
<pre class="code">
<span class="type">VkRenderingAttachmentInfo colorAttachmentInfo</span> = {
    .sType = <span class="enum">VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO</span>,
    .imageView = <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">SwapchainImageView</span>(i),
    .imageLayout = <span class="enum">VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL</span>,
    .loadOp = <span class="enum">VK_ATTACHMENT_LOAD_OP_CLEAR</span>,
    .storeOp = <span class="enum">VK_ATTACHMENT_STORE_OP_STORE</span>,
    .clearValue = { .color = { 1.f, 0.f, 0.f, 1.f } }
};
<span class="type">VkRenderingInfo</span> renderingInfo = {
    .sType = <span class="enum">VK_STRUCTURE_TYPE_RENDERING_INFO</span>,
    .renderArea = { {}, windowSize },
    .layerCount = 1,
    .colorAttachmentCount = 1,
    .pColorAttachments = &amp;colorAttachmentInfo
};
vkCmdBeginRendering(commandBuffer, &amp;renderingInfo);
<span class="fn">vkCmdBindPipeline</span>(commandBuffer, <span class="enum">VK_PIPELINE_BIND_POINT_GRAPHICS</span>, pipeline_triangle);
<span class="fn">vkCmdDraw</span>(commandBuffer, 3, 1, 0, 0);
vkCmdEndRendering(commandBuffer);
</pre>
<p>
    运行程序，验证层没报错便对了。
    <br>
    本节中没有详细讲解的一些参数，今后可能在第八章的用例中进行讲解。
</p>
<p>
    本节的示例代码参见：<a href="https://github.com/EasyVulkan/EasyVulkan.github.io/tree/main/solution/EasyVulkan_Ch6/Ch6-2.hpp">Ch6-2.hpp</a>
</p></section>
<section id="id5">
<h2>多线程动态渲染<a class="headerlink" href="#id5" title="Permalink to this heading"></a></h2>
<p>
    因为先前讲到了可以在多线程中挂起/恢复动态渲染，这里就简单扼要地给个示例。
    <br>
    对C++线程的简单、易用、基础的封装：<a href="https://github.com/EasyVulkan/EasyVulkan.github.io/tree/main/solution/EasyVulkan_Ch6/QueueThread.h">QueueThread.h</a>
</p>
<pre class="code">
<span class="pragma">#pragma once</span>
<span class="pragma">#include</span> <span class="str">&lt;vector&gt;</span>
<span class="pragma">#include</span> <span class="str">&lt;thread&gt;</span>
<span class="pragma">#include</span> <span class="str">&lt;queue&gt;</span>
<span class="pragma">#include</span> <span class="str">&lt;mutex&gt;</span>
<span class="pragma">#include</span> <span class="str">&lt;condition_variable&gt;</span>
<span class="pragma">#include</span> <span class="str">&lt;functional&gt;</span>

<span class="kw">class</span> <span class="type">queueThread</span> {
    <span class="kw">using</span> <span class="type">work_t</span> = std::<span class="type">function</span>&lt;<span class="kw">void</span>()&gt;
    std::<span class="type">thread</span> thread;
    <span class="kw">bool</span> joinable = <span class="kw">true</span>;
    std::<span class="type">queue</span>&lt;<span class="type">work_t</span>&gt; works;
    std::<span class="type">mutex</span> mutex;
    std::<span class="type">condition_variable</span> condition;
    <span class="cmt">//--------------------</span>
    <span class="kw">void</span> <span class="fn">DoWorks</span>() {
        <span class="kw">while</span> (<span class="kw">true</span>) {
            std::<span class="type">unique_lock lock</span>(mutex);
            condition.<span class="fn">wait</span>(lock, [this] { <span class="kw">return</span> !(works.<span class="fn">empty</span>() &amp;&amp; joinable); });
            <span class="kw">if</span> (!joinable)
                <span class="kw">return</span>;
            works.<span class="fn">front</span>()();
            works.<span class="fn">pop</span>();
            condition.<span class="fn">notify_one</span>();
        }
    }
<span class="kw">public</span>:
    <span class="fn">queueThread</span>() {
        thread = std::<span class="type">thread</span>(&amp;queueThread::<span class="fn">DoWorks</span>, <span class="kw">this</span>);
    }
    <span class="fn">queueThread</span>(<span class="type">queueThread</span>&amp;&amp;) = <span class="kw">delete</span>;
    <span class="fn">~queueThread</span>() {
        std::<span class="type">unique_lock lock</span>(mutex);
        condition.<span class="fn">wait</span>(lock, [this] { <span class="kw">return</span> works.<span class="fn">empty</span>(); });
        joinable = <span class="kw">false</span>;
        lock.<span class="type">unlock</span>();
        condition.<span class="type">notify_one</span>();
        thread.<span class="type">join</span>();
    }
    <span class="cmt">//Non-const Function</span>
    <span class="kw">void</span> <span class="fn">PushWork</span>(<span class="type">work_t</span> <span class="par">work</span>) {
        mutex.<span class="fn">lock</span>();
        works.<span class="fn">push</span>(<span class="par">work</span>);
        mutex.<span class="fn">unlock</span>();
        condition.<span class="fn">notify_one</span>();
    }
    <span class="kw">void</span> <span class="fn">Wait</span>() {
        std::<span class="type">unique_lock lock</span>(mutex);
        condition.<span class="fn">wait</span>(lock, [this] { <span class="kw">return</span> works.<span class="fn">empty</span>(); });
    }
};
</pre>
<ul>
    <li>
        <p>
            默认初始化后，用<span class="fn">PushWork</span>(...)将函数对象加入执行队列，用<span class="fn">Wait</span>()等待队列中所有函数对象执行结束。
        </p>
    </li>
</ul>
<p>
    上述代码就不做详细解释了，有兴趣的话请自己啃C++线程库。
    <br>
    包含该头文件后，在适当的位置默认初始化一个<span class="fn">queueThread</span>：
</p>
<pre class="code">
<span class="kw">int</span> <span class="fn">main</span>() {
    <span class="type">PFN_vkCmdBeginRenderingKHR</span> vkCmdBeginRendering = ::<span class="fn">vkCmdBeginRendering</span>;
    <span class="type">PFN_vkCmdEndRenderingKHR</span> vkCmdEndRendering = ::<span class="fn">vkCmdEndRendering</span>;

    <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">UseLatestApiVersion</span>();
    <span class="kw">if</span> (<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">ApiVersion</span>() &lt; <span class="mcr">VK_API_VERSION_1_2</span>)
        <span class="kw">return</span> -1;
    <span class="kw">if</span> (<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">ApiVersion</span>() &lt; <span class="mcr">VK_API_VERSION_1_3</span>) { <span class="cmt">/*...*/</span> }
    <span class="kw">else</span>
        <span class="kw">if</span> (!<span class="fn">InitializeWindow</span>({ 1280, 720 }) ||
            !<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">PhysicalDeviceVulkan13Features</span>().dynamicRendering)
            <span class="kw">return</span> -1;

    <span class="type">queueThread</span> queueThread;

    <span class="cmt">/*...*/</span>
}
</pre>
<p>
    多线程录制命令缓冲区时，各个线程的命令缓冲区需要分配自各自不同的命令池，即每个线程要有专用的命令池（命令池的创建和命令缓冲区的分配在当前线程即可）：
</p>
<pre class="code">
<span class="type">commandBuffer</span> commandBuffers[2];
<span class="type">commandPool</span> commandPool0(<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">QueueFamilyIndex_Graphics</span>(), <span class="enum">VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT</span>);
<span class="type">commandPool</span> commandPool1(<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">QueueFamilyIndex_Graphics</span>(), <span class="enum">VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT</span>);
commandPool0.<span class="fn">AllocateBuffers</span>(commandBuffers[0]);
commandPool1.<span class="fn">AllocateBuffers</span>(commandBuffers[1]);
</pre>
<p>
    然后把渲染流程拆开成能在主线程和另一线程里分别录制的部分。
    <br>
    就清个屏画个三角形的这堆代码，也没啥好拆的，就拆成“在挂起前清屏”和“在恢复后画三角形”好了，在渲染循环前定义分别的lambda：
</p>
<pre class="code">
<span class="kw">auto</span> BeforeSuspending = [&amp;] {
    commandBuffers[0].<span class="fn">Begin</span>(<span class="enum">VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT</span>);

    <span class="type">VkImageMemoryBarrier</span> imageMemoryBarrier = {
        .sType = <span class="enum">VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER</span>,
        .dstAccessMask = <span class="enum">VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT</span>,
        .oldLayout = <span class="enum">VK_IMAGE_LAYOUT_UNDEFINED</span>,
        .newLayout = <span class="enum">VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL</span>,
        .srcQueueFamilyIndex = <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
        .dstQueueFamilyIndex = <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
        .image = <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">SwapchainImage</span>(<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">CurrentImageIndex</span>()),
        .subresourceRange = { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 1, 0, 1 }
    };
    <span class="fn">vkCmdPipelineBarrier</span>(
        commandBuffer,
        <span class="enum">VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT</span>,
        <span class="enum">VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT</span>,
        <span class="enum">VK_DEPENDENCY_BY_REGION_BIT</span>,
        0, <span class="kw">nullptr</span>,
        0, <span class="kw">nullptr</span>,
        1, &amp;imageMemoryBarrier);

    <span class="type">VkRenderingAttachmentInfo</span> colorAttachmentInfo = {
        .sType = <span class="enum">VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO</span>,
        .imageView = <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">SwapchainImageView</span>(<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">CurrentImageIndex</span>()),
        .imageLayout = <span class="enum">VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL</span>,
        .loadOp = <span class="enum">VK_ATTACHMENT_LOAD_OP_CLEAR</span>,
        .storeOp = <span class="enum">VK_ATTACHMENT_STORE_OP_STORE</span>,
        .clearValue = { .color = { 1.f, 0.f, 0.f, 1.f } }
    };
    <span class="type">VkRenderingInfo</span> renderingInfo = {
        .sType = <span class="enum">VK_STRUCTURE_TYPE_RENDERING_INFO</span>,
        .flags = <span class="enum">VK_RENDERING_SUSPENDING_BIT</span>,
        .renderArea = { {}, windowSize },
        .layerCount = 1,
        .colorAttachmentCount = 1,
        .pColorAttachments = &amp;colorAttachmentInfo
    };
    vkCmdBeginRendering(commandBuffers[0], &amp;renderingInfo);
    vkCmdEndRendering(commandBuffers[0]);

    commandBuffers[0].<span class="fn">End</span>();
};
<span class="kw">auto</span> AfterResuming = [&amp;] {
    commandBuffers[1].<span class="fn">Begin</span>(<span class="enum">VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT</span>);

    <span class="type">VkRenderingAttachmentInfo</span> colorAttachmentInfo = {
        .sType = <span class="enum">VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO</span>,
        .imageView = <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">SwapchainImageView</span>(<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">CurrentImageIndex</span>()),
        .imageLayout = <span class="enum">VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL</span>,
        .loadOp = <span class="enum">VK_ATTACHMENT_LOAD_OP_CLEAR</span>,
        .storeOp = <span class="enum">VK_ATTACHMENT_STORE_OP_STORE</span>,
        .clearValue = { .color = { 1.f, 0.f, 0.f, 1.f } }
    };
    <span class="type">VkRenderingInfo</span> renderingInfo = {
        .sType = <span class="enum">VK_STRUCTURE_TYPE_RENDERING_INFO</span>,
        .flags = <span class="enum">VK_RENDERING_RESUMING_BIT</span>,
        .renderArea = { {}, windowSize },
        .layerCount = 1,
        .colorAttachmentCount = 1,
        .pColorAttachments = &amp;colorAttachmentInfo
    };
    vkCmdBeginRendering(commandBuffers[1], &amp;renderingInfo);
    <span class="fn">vkCmdBindPipeline</span>(commandBuffers[1], <span class="enum">VK_PIPELINE_BIND_POINT_GRAPHICS</span>, pipeline_triangle);
    <span class="fn">vkCmdDraw</span>(commandBuffers[1], 3, 1, 0, 0);
    vkCmdEndRendering(commandBuffers[1]);

    <span class="type">VkImageMemoryBarrier</span> imageMemoryBarrier = {
        .sType = <span class="enum">VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER</span>,
        .srcAccessMask = <span class="enum">VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT</span>,
        .oldLayout = <span class="enum">VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL</span>,
        .newLayout = <span class="enum">VK_IMAGE_LAYOUT_PRESENT_SRC_KHR</span>,
        .srcQueueFamilyIndex = <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
        .dstQueueFamilyIndex = <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
        .image = <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">SwapchainImage</span>(<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">CurrentImageIndex</span>()),
        .subresourceRange = { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 1, 0, 1 }
    };
    <span class="fn">vkCmdPipelineBarrier</span>(
        commandBuffer,
        <span class="enum">VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT</span>,
        <span class="enum">VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT</span>,
        <span class="enum">VK_DEPENDENCY_BY_REGION_BIT</span>,
        0, <span class="kw">nullptr</span>,
        0, <span class="kw">nullptr</span>,
        1, &amp;imageMemoryBarrier);

    commandBuffers[1].<span class="fn">End</span>();
};
</pre>
<ul>
    <li>
        <p>
            <code>[&amp;]</code>的意思是指定默认捕获为引用捕获（这里只捕获了<span class="var">commandBuffers</span>），实质上是存储了一个指针常量。
        </p>
    </il>
    <li>
        <p>
            妈的，早知道当初就写个<span class="type">graphicsBase</span>::<span class="fn">CurrentImageView</span>()和<span class="type">graphicsBase</span>::<span class="fn">CurrentImage</span>()了！
        </p>
    </li>
</ul>
<p>
    以防你对并发编程的问题不敏感，注意不能用下面的写法，多线程中对同一变量的并行读写没有明确顺序可言，让两个lambda共用这些信息结构体会导致错误：
</p>
<pre class="code">
<span class="type">VkRenderingAttachmentInfo</span> colorAttachmentInfo = { <span class="cmt">/*...*/</span> };
<span class="type">VkRenderingInfo</span> renderingInfo = { <span class="cmt">/*...*/</span> };
<span class="type">VkImageMemoryBarrier</span> imageMemoryBarrier = { <span class="cmt">/*...*/</span> };

<span class="kw">auto</span> BeforeSuspending = [&amp;] {
    <span class="cmt">/*...*/</span>

    imageMemoryBarrier.srcAccessMask = 0;
    imageMemoryBarrier.dstAccessMask = <span class="enum">VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT</span>;
    imageMemoryBarrier.oldLayout = <span class="enum">VK_IMAGE_LAYOUT_UNDEFINED</span>;
    imageMemoryBarrier.newLayout = <span class="enum">VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL</span>;

    <span class="cmt">/*...*/</span>

    colorAttachmentInfo.imageView = <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">SwapchainImageView</span>(<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">CurrentImageIndex</span>());
    renderingInfo.flags = <span class="enum">VK_RENDERING_SUSPENDING_BIT</span>;
    renderingInfo.renderArea = { {}, windowSize };

    <span class="cmt">/*...*/</span>
};
<span class="kw">auto</span> AfterResuming = [&amp;] {
    <span class="cmt">/*...*/</span>

    colorAttachmentInfo.imageView = <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">SwapchainImageView</span>(<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">CurrentImageIndex</span>());
    renderingInfo.flags = <span class="enum">VK_RENDERING_RESUMING_BIT</span>,
    renderingInfo.renderArea = { {}, windowSize };

    <span class="cmt">/*...*/</span>

    imageMemoryBarrier.srcAccessMask = <span class="enum">VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT</span>;
    imageMemoryBarrier.dstAccessMask = 0;
    imageMemoryBarrier.oldLayout = <span class="enum">VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL</span>;
    imageMemoryBarrier.newLayout = <span class="enum">VK_IMAGE_LAYOUT_PRESENT_SRC_KHR</span>;

    <span class="cmt">/*...*/</span>
};
</pre>
<p>
    如果你觉得代码太冗余，或者日后改起来不方便的话，可以这么写，在每个lambda里复制一份lambda外的结构体，然后再修改这些结构体：
</p>
<pre class="code">
<span class="type">VkImageMemoryBarrier</span> imageMemoryBarrier = {
    .sType = <span class="enum">VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER</span>,
    .srcQueueFamilyIndex = <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
    .dstQueueFamilyIndex = <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>,
    .subresourceRange = { <span class="enum">VK_IMAGE_ASPECT_COLOR_BIT</span>, 0, 1, 0, 1 }
};
<span class="type">VkRenderingAttachmentInfo</span> colorAttachmentInfo = {
    .sType = <span class="enum">VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO</span>,
    <span class="cmt">//imageView将在渲染循环中赋值</span>
    .imageLayout = <span class="enum">VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL</span>,
    .loadOp = <span class="enum">VK_ATTACHMENT_LOAD_OP_CLEAR</span>,
    .storeOp = <span class="enum">VK_ATTACHMENT_STORE_OP_STORE</span>,
    .clearValue = { .color = { 1.f, 0.f, 0.f, 1.f } }
};
<span class="type">VkRenderingInfo</span> renderingInfo = {
    .sType = <span class="enum">VK_STRUCTURE_TYPE_RENDERING_INFO</span>,
    .layerCount = 1,
    .colorAttachmentCount = 1,
    .pColorAttachments = &amp;colorAttachmentInfo
};

<span class="kw">auto</span> BeforeSuspending = [&amp;, imageMemoryBarrier, renderingInfo]() <span class="kw">mutable</span> {
    commandBuffers[0].<span class="fn">Begin</span>(<span class="enum">VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT</span>);

    imageMemoryBarrier.dstAccessMask = <span class="enum">VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT</span>;
    imageMemoryBarrier.newLayout = <span class="enum">VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL</span>;
    imageMemoryBarrier.image = <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">SwapchainImage</span>(<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">CurrentImageIndex</span>());
    <span class="fn">vkCmdPipelineBarrier</span>(
        commandBuffer,
        <span class="enum">VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT</span>,
        <span class="enum">VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT</span>,
        <span class="enum">VK_DEPENDENCY_BY_REGION_BIT</span>,
        0, <span class="kw">nullptr</span>,
        0, <span class="kw">nullptr</span>,
        1, &amp;imageMemoryBarrier);

    renderingInfo.flags = <span class="enum">VK_RENDERING_SUSPENDING_BIT</span>;
    renderingInfo.renderArea = { {}, windowSize };
    vkCmdBeginRendering(commandBuffers[0], &amp;renderingInfo);
    vkCmdEndRendering(commandBuffers[0]);

    commandBuffers[0].<span class="fn">End</span>();
};
<span class="kw">auto</span> AfterResuming = [&amp;, imageMemoryBarrier, renderingInfo]() <span class="kw">mutable</span> {
    commandBuffers[1].<span class="fn">Begin</span>(<span class="enum">VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT</span>);

    renderingInfo.flags = <span class="enum">VK_RENDERING_RESUMING_BIT</span>;
    renderingInfo.renderArea = { {}, windowSize };
    vkCmdBeginRendering(commandBuffers[1], &amp;renderingInfo);
    <span class="fn">vkCmdBindPipeline</span>(commandBuffers[1], <span class="enum">VK_PIPELINE_BIND_POINT_GRAPHICS</span>, pipeline_triangle);
    <span class="fn">vkCmdDraw</span>(commandBuffers[1], 3, 1, 0, 0);
    vkCmdEndRendering(commandBuffers[1]);

    imageMemoryBarrier.srcAccessMask = <span class="enum">VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT</span>;
    imageMemoryBarrier.oldLayout = <span class="enum">VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL</span>;
    imageMemoryBarrier.newLayout = <span class="enum">VK_IMAGE_LAYOUT_PRESENT_SRC_KHR</span>;
    imageMemoryBarrier.image = <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">SwapchainImage</span>(<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">CurrentImageIndex</span>());
    <span class="fn">vkCmdPipelineBarrier</span>(
        commandBuffer,
        <span class="enum">VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT</span>,
        <span class="enum">VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT</span>,
        <span class="enum">VK_DEPENDENCY_BY_REGION_BIT</span>,
        0, <span class="kw">nullptr</span>,
        0, <span class="kw">nullptr</span>,
        1, &amp;imageMemoryBarrier);

    commandBuffers[1].<span class="fn">End</span>();
};
</pre>
<ul>
    <li>
        <p>
            <code>[&amp;, imageMemoryBarrier, renderingInfo]</code>的意思是指定默认捕获为引用捕获，然后复制捕获<span class="var">imageMemoryBarrier</span>和<span class="var">renderingInfo</span>，<span class="kw">mutable</span>使得复制捕获的变量可修改。
        </p>
    </il>
</ul>
<p>
    复制捕获的变量被存储为函数对象的成员变量。每次执行函数对象时，复制捕获的变量不会被重置到捕获时的原值，所以这里诸如<code>renderingInfo.flags = <span class="enum">VK_RENDERING_RESUMING_BIT</span>;</code>等，等号右边是常量的语句其实是在每次执行该lambda的过程中进行着重复赋值。
    <br>
    （笔者并不太喜欢有捕获的lambda，不过要去掉捕获，还得对代码做一些我懒得说明的修改，就这样了！）
</p>
<p>
    顺着上述代码继续往下写，两个lambda里所需的图像附件的信息是一致的，所以就不做复制捕获了。两个lambda中的<span class="var">renderingInfo.pColorAttachments</span>指向了同一个<span class="type">VkRenderingAttachmentInfo</span>，只需在渲染循环中更新<span class="var">colorAttachmentInfo.imageView</span>即可：
</p>
<pre class="code">
<span class="cmt">/*...前面略*/</span>

<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">SwapImage</span>(semaphore_imageIsAvailable);
colorAttachmentInfo.imageView = <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">SwapchainImageView</span>(<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">CurrentImageIndex</span>());

<span class="cmt">/*...后面略*/</span>
</pre>
<p>
    把<span class="var">AfterResuming</span>先扔进另一个线程开始执行，然后在主线程中执行<span class="var">BeforeSuspending</span>试试。
    <br>
    再在<span class="var">BeforeSuspending</span>后等待另一个线程中的<span class="var">AfterResuming</span>执行结束：
</p>
<pre class="code">
queueThread.<span class="fn">PushWork</span>(AfterResuming);
BeforeSuspending();
queueThread.<span class="fn">Wait</span>();
</pre>
<p>
    然后一次性提交两个缓冲区即可，<span class="var">commandBuffers[0]</span>对应的是到挂起为止，<span class="var">commandBuffers[1]</span>对应的是恢复之后，提交顺序上没有问题：
</p>
<pre class="code">
<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">SwapImage</span>(semaphore_imageIsAvailable);
colorAttachmentInfo.imageView = <span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">SwapchainImageView</span>(<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">CurrentImageIndex</span>());

queueThread.<span class="fn">PushWork</span>(AfterResuming);
BeforeSuspending();
queueThread.<span class="fn">Wait</span>();

<span class="kw">static constexpr</span> <span class="type">VkPipelineStageFlags</span> waitDstStage = <span class="enum">VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT</span>;
<span class="type">VkSubmitInfo</span> submitInfo = {
    .waitSemaphoreCount = 1,
    .pWaitSemaphores = semaphore_imageIsAvailable.<span class="fn">Address</span>(),
    .pWaitDstStageMask = &amp;waitDstStage,
    .commandBufferCount = 2,
    .pCommandBuffers = commandBuffers[0].<span class="fn">Address</span>(),
    .signalSemaphoreCount = 1,
    .pSignalSemaphores = semaphore_renderingIsOver.<span class="fn">Address</span>(),
};
<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">SubmitCommandBuffer_Graphics</span>(submitInfo, fence);
<span class="type">graphicsBase</span>::<span class="sfn">Base</span>().<span class="fn">PresentImage</span>(semaphore_renderingIsOver);
</pre>
<p>
    运行程序，还是一样的红背景和蓝三角形，如果蓝三角形没了，那说明某些地方顺序出问题了（可能先画三角形再清了屏）。
    <br>
    就清屏+画三角形这种程度的事，多线程录制命令对程序执行效率完全不会有提升。。。
</p>
<p>
    示例代码参见：<a href="https://github.com/EasyVulkan/EasyVulkan.github.io/tree/main/solution/EasyVulkan_Ch6/Ch6-2_MT.hpp">Ch6-2_MT.hpp</a>
</p></section>
</section>


           </div>
          </div>
          <footer><div class="rst-footer-buttons" role="navigation" aria-label="Footer">
        <a href="Ch6-1%20%E6%97%A0%E5%9B%BE%E5%83%8F%E5%B8%A7%E7%BC%93%E5%86%B2.html" class="btn btn-neutral float-left" title="Ch6-1 无图像帧缓冲" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> 上一页</a>
        <a href="Ch7-1%20%E5%88%9D%E8%AF%86%E9%A1%B6%E7%82%B9%E7%BC%93%E5%86%B2%E5%8C%BA.html" class="btn btn-neutral float-right" title="Ch7-1 初识顶点缓冲区" accesskey="n" rel="next">下一页 <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
    </div>

  <hr/>

  <div role="contentinfo">
    <p>&#169; 版权所有 2021-2025, 葉橙.</p>
  </div>

  利用 <a href="https://www.sphinx-doc.org/">Sphinx</a> 构建，使用了 
    <a href="https://github.com/readthedocs/sphinx_rtd_theme">主题</a>
    由 <a href="https://readthedocs.org">Read the Docs</a>开发.
   

</footer>
        </div>
      </div>
    </section>
  </div>
  <script>
      jQuery(function () {
          SphinxRtdTheme.Navigation.enable(true);
      });
  </script> 

</body>
</html>